package org.ricks.classfile;

import org.ricks.ioc.event.IEventReceiver;

import java.lang.classfile.*;
import java.lang.classfile.constantpool.MethodRefEntry;
import java.lang.constant.*;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class ProxyGenerator {

    private final static ClassDesc OBJECT_CLASS_DESC =  ClassDesc.of(Object.class.getName());

    public static <T> T generateProxy(Class<T> targetClass) throws Exception {
         ClassDesc proxyDesc = ClassDesc.of(targetClass.getName() + "Proxy");

        byte[] bytecode = ClassFile.of().build(proxyDesc, classBuilder -> {
            classBuilder.withSuperclass(ClassDesc.of(targetClass.getName())); // 继承目标类

            // 遍历目标类方法，生成代理方法
            for (var method : targetClass.getDeclaredMethods()) {
                if (method.isAnnotationPresent(ProxyMethod.class)) {
                    classBuilder.withMethod(
                            method.getName(),
                            MethodTypeDesc.of(ClassDesc.of(method.getReturnType().getName())),
                            ClassFile.ACC_PUBLIC,
                            methodBuilder -> injectProxyLogic(methodBuilder, method,proxyDesc)
                    );
                }
            }
        });

        // 定义类
        MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(targetClass, MethodHandles.lookup());
        Class<?> clazz = lookup.defineClass(bytecode);

        // 创建实例
        Constructor<?> constructor = clazz.getConstructor();
        return (T) constructor.newInstance();

        // 动态加载代理类
//        return (Class<T>) MethodHandles.lookup().defineClass(bytecode);
    }

    private static void injectProxyLogic(MethodBuilder methodBuilder,
                                         Method targetMethod,
                                         ClassDesc proxyDesc) {
        methodBuilder.withCode(codeBuilder -> {
            codeBuilder
                    .aload(0)
                    .getfield(proxyDesc, "handler", ClassDesc.of(InvocationHandler.class.getName()))
                    .aload(0)
                    // 加载目标类Class对象
                    .ldc(ClassDesc.of(targetMethod.getDeclaringClass().getName()))
                    // 加载方法名
                    .ldc(targetMethod.getName())
                    // 创建参数类型数组
//                    .iconst(targetMethod.getParameterCount())
                    .anewarray(ClassDesc.of(Class.class.getName()));

            // 填充参数类型到数组
            Class<?>[] paramTypes = targetMethod.getParameterTypes();
            for (int i = 0; i < paramTypes.length; i++) {
                codeBuilder
                        .dup()
//                        .iconst(i)
                        .ldc(ClassDesc.of(paramTypes[i].getName()))
                        .aastore();
            }

            codeBuilder
                    // 调用Class.getDeclaredMethod(String, Class[])
                    .invokevirtual(ClassDesc.of(Class.class.getName()), "getDeclaredMethod",
                            MethodTypeDesc.of(ClassDesc.of(Method.class.getName()),
                                    ClassDesc.of(String.class.getName()),
                                    ClassDesc.ofDescriptor(Class[].class.getName())))
                    // 参数3：args数组
                    .aload(1)
                    .invokevirtual(
                            ClassDesc.of(InvocationHandler.class.getName()),
                            "invoke",
                            MethodTypeDesc.of(
                                    ClassDesc.of(Object.class.getName()),
                                    ClassDesc.of(Object.class.getName()),
                                    ClassDesc.of(Method.class.getName()),
                                    ClassDesc.ofDescriptor(Object[].class.getName()))
                    )
                    .areturn();
        });
    }


}
